Перейти к основному содержимому

5.01. Циклы

Разработчику Архитектору

Циклы

Циклы: while, do...while, for, for...in, for...of, for await...of
Управление потоком: break, continue, return, throw

Цикл – многократно повторяемая процедура. Она может быть с известным количеством итераций и с неопределённым (бесконечным). То есть, либо мы знаем, когда начинать, сколько раз повторить, либо повторяем снова и снова, пока что-то не изменится.

for используется для определённого количества операций, с указанием начального значения, условия и шага каждой итерации:

for (начало; условие; шаг) {
// Тело цикла
}

Так, это похоже на функцию, где есть три выражения:

  • начало задаёт начальное состояние цикла. Обычно здесь объявляется и инициализируется счётчик, который выполняется один раз перед началом. В начале можно как объявить переменную, так и использовать существующую;
  • условие, которое проверяется перед каждой итерацией. Если это условие true (истинно), цикл выполняется, если false – цикл останавливается. Если условие пропустить, цикл будет выполняться бесконечно. Тут можно использовать любые логические выражения (<, >, <=, => и т.д.);
  • шаг, который выполняется после каждой итерации и меняет счётчик.

Пример:

for (let i = 0; i < 5; i++) {
console.log(i); // Выведет 0, 1, 2, 3, 4
}

Как это работает?

  • for – ключевое слово, указывающее на начало цикла;
  • let i = 0 – это начало, создание переменной с именем i и со значением 0;
  • i < 5 – это условие, и если i < 5 то будет true и тело цикла выполняется;
  • console.log(i) – это тело цикла, которое выводит в консоль значение переменной i;
  • i++ - инкремент, когда значение увеличивается на 1;
  • повторная проверка будет выполняться снова и снова;
  • если проверка true – цикл запускается снова, и снова выполняется тело.

Шаг — это выражение, которое изменяет значение переменной-счётчика на каждой итерации. Обычно он стоит на третьем месте в заголовке цикла for.

Как можно описать шаг?

ШагОписание
i++увеличивает i на 1
i--уменьшает i на 1
i += 2увеличивает i на 2
i -= 3уменьшает i на 3
i *= 2умножает i на 2
i /= 10делит i на 10

++ и -— это инкремент и декремент. Это специальные операторы для увеличения или уменьшения значения на 1. Бывают пост- и пре- операторы:

  • i++ это постинкремент;
  • ++i это преинкремент;
  • i-— это постдекремент;
  • --i это предекремент. Разница между пре- и постформами важна только если результат используется сразу. Большинство этих операций (++, --, +=, -=, *=, /=) работают с числами , но JavaScript достаточно гибкий, и эти операторы могут работать и с другими типами данных.

Основное использование, конечно, числа, к примеру x+=3.

Со строками работает только +=, оператор работает как конкатенация строк:

let str = 'Привет';
str += ', мир!'; // 'Привет, мир!'

Но ++, --, -=, *= и т.д. со строками работать не будут корректно , так как JS попытается привести строку к числу, что может привести к NaN.

С объектами и массивами такие операции не имеют смысла. Поэтому грамотно подходите к выбору циклов - для других операций

Для работы с массивами лучше использовать forEach (в других языках он является полноценным циклом, тогда как в JavaScript это просто метод объектов-массивов), который озволяет перебрать все элементы массива , выполнив для каждого элемента заданную функцию (т.н. callback-функцию).

Он удобен и читабелен, особенно когда не нужно управлять индексами вручную, как в классическом for. Синтаксис у него таков:

array.forEach(function(element, index, array) {
// тело функции
});

Здесь мы вызываем функцию с параметрами:

  • element - текущий элемент массива, обязательный;
  • index - индекс текущего элемента (начинается с 0);
  • array - сам массив, по которому идёт перебор.

Пример:

const fruits = ['яблоко', 'банан', 'груша'];

fruits.forEach(function(fruit) {
console.log(fruit);
});

// Вывод:
// яблоко
// банан
// груша

Функцию можно сделать стрелочной:

const numbers = [1, 2, 3];

numbers.forEach(num => console.log(num * 2));

// Вывод:
// 2
// 4
// 6

forEach не мутирует (не изменяет) исходный массив, break здесь не нужен (он его просто не поддерживает), а код будет более читабельным. Так что можно сказать, что в JS есть цикл foreach, но он просто метод для работы с массивами, для перебора элементов.

while используется, когда количество итераций неизвестно. Код будет выполняться то тех пор, пока условие true.

let i = 0;

while (i < 5) {
console.log(i); // Выведет 0, 1, 2, 3, 4
i++;
}

В данном случае, пока i не будет равным 5, будет выводиться значение i в консоль. Принцип такой же, как и в for, однако он не включает в себя объявление и цикл – всё, что нужно выполнять, выведено в тело цикла, в том числе i++ (инкремент) – «Пока условие истинно, делай вот это».

do…while отличается от while тем, что тело цикла выполнится хотя бы один раз, даже если условие false.

let i = 0;

do {
console.log(i); // Выведет 0
i++;
} while (i < 0); // Условие false, но цикл сработал 1 раз

Здесь заметно, что цикл начинается с ключевого слова do и заканчивается while. Это значит, что принцип такой – «Сделай вот это. Повторяй, пока условие истинно».

Таблица отличий циклов

ЦиклКогда использоватьОсобенности
forИзвестно количество итерацийКомпактный синтаксис
whileНеизвестно количество итерацийМожет не выполниться ни разу
do…whileПроверка после итерацииВыполнится минимум 1 раз

Операции выполняются в рамках функций, а циклы и условные операторы – части функций, словно инструменты. Каждая функция выполняется в соответствии с её телом. В общем случае JS работает в одном потоке, и долгие операции могут замораживать страницу, поэтому надо разбивать тяжёлые задачи на части. Однако, в ряде случаев, браузер «зависает» именно при ожидании результата выполнения функции. Тут мы подходим к асинхронности.